﻿using System;
using System.Collections.Generic;
using System.Linq;
using data.contracts;

namespace modelbuilder.parts
{
    public class ExpandSplitWires : IExpandSplitWires
    {
        private const char PinSeparator = ';';
        public event Action<IEnumerable<Board>> Out_Result;

        public void In_Process(IEnumerable<Board> boards) {
            Out_Result(ExpandAllSplitWires(boards));
        }

        private static IEnumerable<Board> ExpandAllSplitWires(IEnumerable<Board> boards) {
            return boards.Select(ExpandAllSplitWires);
        }

        private static Board ExpandAllSplitWires(Board board) {
            var wires = ExpandAllSplitWires(board.Wires.ToArray());
            board.ReplaceWires(wires);
            return board;
        }

        private static IEnumerable<Wire> ExpandAllSplitWires(IEnumerable<Wire> wires) {
            var number = 1;
            foreach (var wire in wires) {
                if (wire.To.Contains(PinSeparator)) {
                    foreach (var newWire in ExpandSplitWire(wire, number++)) {
                        yield return newWire;
                    }
                }
                else {
                    yield return wire;
                }
            }
        }

        private static IEnumerable<Wire> ExpandSplitWire(Wire wire, int number) {
            var types = ExtractTypeInformation(wire.Type);
            var splitType = string.Format("{0},{1},{2}", types.Item1, types.Item2, types.Item3);
            var splitName = string.Format("$split{0}", number);
            var splitter = string.Format("(ISplit<{0}>) {1}", splitType, splitName);
            var outputPins = wire.To.Split(PinSeparator);

            yield return new Wire(wire.From, types.Item1, splitter + ".Input");
            yield return new Wire(splitter + ".Output1", types.Item2, outputPins[0].Trim());
            yield return new Wire(splitter + ".Output2", types.Item3, outputPins[1].Trim());
        }

        private static Tuple<string,string,string> ExtractTypeInformation(string type) {
            var types = type.Split(':');
            if (types.Length == 1) {
                return new Tuple<string, string, string>(type, type, type);
            }
            // type="Tuple{string, string} : (string; string)"
            var inputType = types[0].Trim();
            var outputTypes = types[1].Trim().TrimStart('(').TrimEnd(')').Split(PinSeparator);
            return new Tuple<string, string, string>(inputType, outputTypes[0].Trim(), outputTypes[1].Trim());
        }
    }
}